// BinaryStaticBarrier.cpp : Defines the entry point for the console application.
//

// StaticBinaryStaticTreeBarrier.cpp
//

#include "stdafx.h"
#include "BinaryStaticTreeBarrier.h"
#include "../mpp_workshop/Test.h"
#include "../mpp_workshop/Globals.h"
//#include <iostream>
#include <stdio.h>

using namespace std;

BinaryStaticTreeBarrier *g_bar = new BinaryStaticTreeBarrier(Globals::numberOfThreads);

/*
 * builds the tree as we build a heap
 * @param depth The level in the binary tree Node would be created.
 * threads are in the _node array, 0 is root.
 */
void BinaryStaticTreeBarrier::build(int depth)
{
	int i;
	Node* parent;
	for (i = 1; i < _numOfThreads ; i++)
	{
		// parent of node i
		int parentIndex = getParentIdx(i);
		parent = _node[parentIndex];
		// create node i
		_node[i] = new Node(parent);
		// increase counter of children
		parent->addChild();

		// if my child is out of the range - mark it as it is done.
		// the children counter should 
		int leftChildIdx = getLeftChildIdx(i);
		int rightChildIdx = getRightChildIdx(i);
		if (leftChildIdx > _numOfThreads - 1)
		{
			_node[i]->markChildDone(LEFT_CHILD_ID);
		}
		if (rightChildIdx > _numOfThreads - 1)
		{
			_node[i]->markChildDone(RIGHT_CHILD_ID);
		}

		//For debug:
		assert( !((leftChildIdx > _numOfThreads - 1) && (rightChildIdx < _numOfThreads)));

	}

	//DEBUG
	//for (i = 0 ; i < _numOfThreads ; i++) {
	//	cout << "node " << i << " parent idx: " << getParentIdx(i) << " children=" << _node[i]->getChildren() << endl;
	//}
}

void BinaryStaticTreeBarrier::Node::await()
{
	int mySense = g_bar->threadSense.get();

	switch (children)
	{
	case 2:
		while (rightChildNotDone.get()) {
			//	Thread::yield();  // use this when running on core2 due machine.
		}
		rightChildNotDone.set(true); // mark for next phase
		// no break on purpose
	case 1:
		while (leftChildNotDone.get()) 
		{ 
		//	Thread::yield();  // use this when running on core2 due machine.
		}
		leftChildNotDone.set(true); // mark for next phase
		break;
	default:
		break;
	}

	if (parent == NULL) { // root?
		g_bar->global_sense = mySense;   // am root: toggle global sense
	} else {
		int me = g_bar->threadId.get();
		parent->markChildDone(me); // indicate child subtree completion
		while (g_bar->global_sense != mySense) 
		{
			//	Thread::yield();  // use this when running on core2 due machine.
		}; // wait for global sense to change
	}

	// toggle my own sense
	int myNewSense = (mySense + 1) % 2;
	g_bar->threadSense.set(myNewSense);
}

void BinaryStaticTreeBarrier::await()
{
	int me = threadId.get();
	_node[me]->await();
}

/**
* @param childIndex
* @return Index of the parent of given childIndex in the node array
*/
const int BinaryStaticTreeBarrier::getParentIdx(int childIndex)
{
	// return Floor( (childIndex+1)/2 - 1 )
	int ret = ( (childIndex + 1 ) >> 1) - 1;
	return ret;
}

/**
* @param parentIdx
* @return
*/
const int BinaryStaticTreeBarrier::getLeftChildIdx(int parentIdx)
{
	// return ((parentIdx + 1) * 2) - 1;
	int ret = ((parentIdx + 1) << 1) - 1;
	return ret;

}

/**
* @param parentIdx index of parent node in node[]
* @return Index to the right child in node[]
*/
const int BinaryStaticTreeBarrier::getRightChildIdx(int parentIdx) {
	// return (parentIdx + 1) * 2;
	int ret = (parentIdx + 1) << 1;
	return ret;
}


void BinaryStaticTreeBarrier::Node::markChildDone(int childThreadId)
{
	if (childThreadId % 2 == 0) {
		rightChildNotDone.set(false);
	} else {
		leftChildNotDone.set(false); 
	}
}


// MAIN
// first arg	= number of threads
// second arg	= max count to 
// third arg	= number of iterations 
// forth arg	= test type 0=counter, 1=localCounter 2=matrix
// fifth arg	= shared memory - 1 for shared mem, 0 for local mem.
// sixth arg	= barrier type. 0=SenseWithWait, 1=LinearSense, 2=BinaryStaticBarrier
int main(int argc,char* argv[])
{
	TestFactory factory;
	TestType testType = Counter; // default test type
	if (argc > 1)
	{
		Globals::numberOfThreads = atoi(argv[1]);
	}
	
	if (argc > 2)
	{
		Globals::countTo = atoi(argv[2]);
	}

	if (argc > 3)
	{
		Globals::numberOfIterations = atoi(argv[3]);
	}

	if (argc > 4)
	{
		int typeId = atoi(argv[4]);
		if (typeId == 0)
		{
			testType = Counter;
		} else if (typeId == 1)
		{
			testType = LocalCounter;
		} else if (typeId == 2)
		{
			testType = Matrix;
		} else {
			printf("Unsupported TestType id: %d. Exiting.\n", typeId);
			assert(false);
		}
	}

	printf("BinaryStaticTreeBarrier, Number of threads, %d, count to %d\n", Globals::numberOfThreads, Globals::countTo);
	fflush(stdout);
	g_bar = new BinaryStaticTreeBarrier(Globals::numberOfThreads);

	Test* test = factory.createTest(g_bar, testType);
	
	test->performTest();
}
